// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "BufferWriter.h"
#include <cstring>
#include "HWSecure/include/securec.h"
#include "Log/logging.h"

/**
* @brief : BufferWriter construtor,init the member variables:m_data m_len m_writePos
* @param [in]data: first object :the data
                   second object:the length of data
*/
BufferWriter::BufferWriter(std::pair<uint8_t*, size_t> data)
    : m_data(data.first), m_len(data.second), m_writePos(0)
{
}

/**
* @brief : BufferWriter construtor
* @param [in]buf: VmiBuffer
*/
BufferWriter::BufferWriter(const VmiBuffer& buf)
    : m_data(buf.GetPointer()), m_len(buf.GetSize()), m_writePos(0)
{
}

/**
* @brief : BufferWriter destructor
*/
BufferWriter::~BufferWriter()
{
    m_data = nullptr;
    m_len = 0;
    m_writePos = 0;
}

/**
* @brief : clear the buffer
*/
void BufferWriter::Clear()
{
    m_writePos = 0;
    errno_t ret = memset_s(m_data, m_len, 0, m_len);
    if (ret != EOK) {
        ERR("memset_s data failed! %d", ret);
        m_errno = ERROR;
    }
}

/**
* @brief : overload operator <<
* @param [in] bufferData: buffer to write
* @return : object BufferWriter
*/
BufferWriter& BufferWriter::operator<<(VmiBuffer bufferData)
{
    if (Remain() < bufferData.m_bufferSize) {
        ERR("m_bufferSize is longer than remain size");
        m_errno = ERROR;
        return *this;
    }

    if (bufferData.m_pointer == nullptr) {
        ERR("m_pointer is nullptr");
        m_errno = ERROR;
        return *this;
    }

    if (IsDataNull()) {
        ERR("m_data is null");
        m_errno = ERROR;
        return *this;
    }
    errno_t ret = memcpy_s(&m_data[m_writePos], Remain(),
        bufferData.m_pointer, bufferData.m_bufferSize);
    if (ret != EOK) {
        ERR("memcpy_s data failed! %d", ret);
        m_errno = ERROR;
        return *this;
    }
    m_writePos += bufferData.m_bufferSize;
    return *this;
}

/**
* @brief : get the Write position
* @return : the Write position
*/
size_t BufferWriter::GetWritePosition() const
{
    return m_writePos;
}

/**
* @brief : set the Write position and return it
* @param [in]pos: the Write position
* @return : the Write position
*/
size_t BufferWriter::SetWritePosition(size_t pos)
{
    if (pos > m_len) {
        ERR("SetWritePosition error pos = %zu, m_len = %zu", pos, m_len);
        m_errno = ERROR;
        return m_writePos;
    }
    m_writePos = pos;
    return m_writePos;
}

/**
* @brief : judge if the data address is null
* @return : true: the data address is null,
            false:the data address is no null,
*/
bool BufferWriter::IsDataNull() const
{
    return m_data == nullptr;
}

/**
* @brief : get the length of data which is waiting to write
* @return : the length
*/
size_t BufferWriter::Length() const
{
    return m_len;
}

/**
* @brief : get the length of remaining data
* @return : the length of remaining data
*/
size_t BufferWriter::Remain() const
{
    return (m_writePos >= m_len) ? 0 : (m_len - m_writePos);
}

/**
* @brief : template converts uint8_t
* @param [in]src: first object:the data to be writed
                  second object:the length of data
*/
template <class T> void BufferWriter::Append(std::pair<const T*, size_t> src) const
{
    std::pair<const uint8_t *, size_t> srcData(reinterpret_cast<const uint8_t *>(src.first), src.second * sizeof(T));
    return Append(srcData);
}

/**
* @brief : append the data
* @param [in]src: first object:the data to be writed
                  second object:the length of data
*/
void BufferWriter::Append(std::pair<const uint8_t*, size_t> src)
{
    const uint8_t* data = src.first;
    size_t dataLen = src.second;
    if (data == nullptr) {
        ERR("append data address is nullptr");
        m_errno = ERROR;
        return;
    }

    if (dataLen == 0) {
        ERR("append data error size is 0");
        m_errno = ERROR;
        return;
    }

    if (IsDataNull()) {
        ERR("m_data is null");
        m_errno = ERROR;
        return;
    }

    if (dataLen > Remain()) {
        ERR("Append error, dataLen > Remain(), dataLen=%zu, Remain()=%zu", dataLen, Remain());
        m_errno = ERROR;
        return;
    }

    errno_t ret = memcpy_s(&m_data[m_writePos], dataLen, data, dataLen);
    if (ret != EOK) {
        ERR("memcpy_s data failed, ret = %d!", ret);
        m_errno = ERROR;
        return;
    }
    m_writePos += dataLen;
}

/**
* @brief : skip data of specified length
* @param [in]src: the specified length
*/
void BufferWriter::Skip(size_t skip)
{
    if (skip > Remain()) {
        ERR("skip > Remain() skip = %zu, Remain = %zu", skip, Remain());
        m_errno = ERROR;
        return;
    }

    m_writePos += skip;
}

/**
 * @brief : get error number
 * @return : error number
 */
int BufferWriter::GetErrorNumber() const
{
    return m_errno;
}

/**
 * @brief : reset error number
 */
void BufferWriter::ResetErrorNumber()
{
    m_errno = OK;
}

